---------The Great Number Chase--------
A 4am crack                  2017-05-09
---------------------------------------

Name: The Great Number Chase
Genre: educational
Year: 1984
Credits: Eric Soldan
Publisher: Milliken Publishing Company
Platform: Apple ][+ or later
Media: single-sided 5.25-inch floppy
OS: Diversi-DOS C1982
Previous cracks: none

                   ~

               Chapter 0
   In Which The Tools Do Not Save Us


This disk was automatically cracked by
Passport. Here is the transcript:

                 --v--

READING FROM S6,D1
T00,S00 FOUND DIVERSI-DOS BOOTLOADER
USING DISK'S OWN RWTS
WRITING TO S5,D2
T00,S03,$91: DF -> DE
T00,S03,$35: DF -> DE
T00,S06,$AE: DF -> DE
T00,S02,$9E: DF -> DE
CRACK COMPLETE.

                 --^--

[Narrator]
But the crack was not complete.

The copy that Passport produces is in a
standard format, fully readable by
third-party tools. But when I boot it,
the disk sounds like it loads DOS as
usual, but then it reboots. Which is
odd, because disks generally do not
reboot unless someone tells them to.

                   ~

               Chapter 1
       In Which We Learn The How
            But Not The Why


Turning to my trusty Disk Fixer sector
editor, I start my investigation as any
investigation should start: on track 0,
sector 0. It is identical to an
unprotected DOS disk.

On to sector 1, then.

                 --v--

T00,S01
----------- DISASSEMBLY MODE ----------
; totally normal -- setting up the RWTS
; parameters to read DOS from tracks
; $00-$02
0000:8E E9 B7       STX   $B7E9
0003:8E F7 B7       STX   $B7F7
0006:A9 01          LDA   #$01
0008:8D F8 B7       STA   $B7F8
000B:8D EA B7       STA   $B7EA
000E:AD E0 B7       LDA   $B7E0
0011:8D E1 B7       STA   $B7E1
0014:A9 02          LDA   #$02
0016:8D EC B7       STA   $B7EC
0019:A9 04          LDA   #$04
001B:8D ED B7       STA   $B7ED
001E:AC E7 B7       LDY   $B7E7
0021:88             DEY
0022:8C F1 B7       STY   $B7F1
0025:A9 01          LDA   #$01
0027:8D F4 B7       STA   $B7F4
002A:8A             TXA
002B:4A             LSR
002C:4A             LSR
002D:4A             LSR
002E:4A             LSR
002F:AA             TAX
0030:A9 00          LDA   #$00
0032:9D F8 04       STA   $04F8,X
0035:9D 78 04       STA   $0478,X

; call RWTS to load DOS from T00-T02
; (my non-working copy sounded like it
; got this far)
0038:20 93 B7       JSR   $B793
003B:A2 FF          LDX   #$FF
003D:9A             TXS
003E:8E EB B7       STX   $B7EB

; nothing suspicious, just sets up some
; machine stuff and jumps back to next
; instruction (not shown)
0041:4C C8 BF       JMP   $BFC8
0044:20 89 FE       JSR   $FE89

; hey ho, hey ho, this custom code has
; got to go
0047:4C 0E BF       JMP   $BF0E

Page $BF is loaded from T00,S09, so
let's jump over there and see what's
going on. This is right about where my
non-working copy stopped, you know,
working.

                 --v--

T00,S09
----------- DISASSEMBLY MODE ----------
; set up RWTS for an unusual sector
; read from T02,S00
000E:A9 02          LDA   #$02
0010:8D EC B7       STA   $B7EC
0013:A9 00          LDA   #$00
0015:8D ED B7       STA   $B7ED
0018:8D EB B7       STA   $B7EB

; no wait, we're just seeking (RWTS
; command 0 = seek), which is totally
; suspicious
001B:8D F4 B7       STA   $B7F4
001E:A9 B7          LDA   #$B7
0020:A0 E8          LDY   #$E8
0022:20 00 BD       JSR   $BD00

; turn on drive motor manually (the
; RWTS would have turned it off before
; returning)
0025:AE F8 05       LDX   $05F8
0028:BD 8C C0       LDA   $C08C,X
002B:BD 89 C0       LDA   $C089,X

; possibly two different counters? not
; sure yet
002E:A9 12          LDA   #$12
0030:85 02          STA   $02
0032:A0 14          LDY   #$14

; look for an $FB nibble (why?)
0034:BD 8C C0       LDA   $C08C,X
0037:10 FB          BPL   $0034
0039:C9 FB          CMP   #$FB
003B:D0 F7          BNE   $0034

; burn some CPU cycles (I may get to
; count these later)
003D:48             PHA
003E:68             PLA
003F:48             PHA
0040:68             PLA
0041:EA             NOP
0042:EA             NOP
0043:EA             NOP
0044:EA             NOP
0045:EA             NOP
0046:EA             NOP

; check the data latch again (note: no
; BPL here, so we're just fetching the
; current value at this moment in time)
0047:BD 8C C0       LDA   $C08C,X

; check if data latch is still $FB
004A:C9 FB          CMP   #$FB

; nope -> skip next instruction
004C:D0 02          BNE   $0050

; decrement one counter (in zp$02)
004E:C6 02          DEC   $02

; decrement the other counter (in Y)
0050:88             DEY

; loop back until done
0051:D0 E1          BNE   $0034

; check the first counter
0053:24 02          BIT   $02

; if it's still positive, that's bad
0055:10 03          BPL   $005A

; if it's negative, continue with the
; rest of the boot process
0057:4C 84 9D       JMP   $9D84

; failure path ends up here --
; decrement what appears to be an
; uninitialized counter and loop back
; to try again some number of times
005A:CE A1 81       DEC   $81A1
005D:D0 CF          BNE   $002E

; eventually fall through to here and
; reboot
005F:AD E9 B7       LDA   $B7E9
0062:4A             LSR
0063:4A             LSR
0064:4A             LSR
0065:4A             LSR
0066:09 C0          ORA   #$C0

; pretty sure this is a bug and it's
; supposed to be altering the next JMP
; instruction, but the address is wrong
0068:8D A0 81       STA   $81A0

; anyway, reboot from slot 6 (always,
; since we didn't actually change it)
006B:4C 00 C6       JMP   $C600

OK, well that's definitely a protection
check, and it's definitely the cause of
my non-working copy not, you know,
working. But what's going on? To answer
that, I turn to my trusty Copy II Plus
nibble editor to look at track $02 on
the original disk.

                   ~

               Chapter 2
       In Which We Learn The Why


(Copy II Plus shows timing bits after a
nibble by showing them in inverse. To
represent this in plain text, I've
replaced them with "+" signs instead.)

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK: 02  START: 1800  LENGTH: 3DFF

3F38: FF+FF+FF+FF+FF+FF+FF+FF+  VIEW
3F40: D5 AA 96 FF FE AB AA AF
      ^^^^^^^^ ^^^^^ ^^^^^ ^^
 addr prologue V=254 T=$02 S=$0F

3F48: AF FB FB DF AA EB FF+9F
      ^^ ^^^^^ ^^^^^^^^
         chksm epilogue

3F50: F9 FE+FF+FF+FF+D5 AA AD+
                     ^^^^^^^^
                   data prologue

3F58: FB+FB+FB+FB+FB+FB+FB+FB+
3F60: FB+FB+FB+FB+FB+FB+FB+FB+
3F68: FB+FB+FB+FB+FB+FB+FB+FB+
3F70: FB+FB+FB+FB+FB+FB+FB+FB+
3F78: FB+FB+FB+FB+FB+FB+FB+FB+
3F80: FB+FB+FB+FB+FB+FB+FB+FB+
3F88: FB+FB+FB+FB+FB+FB+FB+FB+
3F90: FB+FB+FB+FB+FB+FB+FB+FB+
3F98: FB+FB+FB+FB+FB+FB+FB+FB+
3FA0: FB+FB+FB+FB+FB+FB+FB+FB+
3FA8: FB+FB+FB+FB+FB+FB+96 96
3FB0: 96 96 96 96 96 96 96 96

                 --^--

> > Look at all those timing bits there
> > are at least ten.

> Well, you're not wrong.

So we're looking for $FB nibbles and
counting how many of them have a timing
bit after them. How can we tell? By
wasting just the right amount of time.

Returning to this code at $BF34, this
time with cycle counts in the margin:

                 --v--

0034:BD 8C C0       LDA   $C08C,X   ; 4
0037:10 FB          BPL   $0034     ; 2
0039:C9 FB          CMP   #$FB      ; 2
003B:D0 F7          BNE   $0034     ; 2
003D:48             PHA             ; 3
003E:68             PLA             ; 4
003F:48             PHA             ; 3
0040:68             PLA             ; 4
0041:EA             NOP             ; 2
0042:EA             NOP             ; 2
0043:EA             NOP             ; 2
0044:EA             NOP             ; 2
0045:EA             NOP             ; 2
0046:EA             NOP             ; 2
0047:BD 8C C0       LDA   $C08C,X
004A:C9 FB          CMP   #$FB

                 --^--

The way the data latch ($C08C,X) works
is that it accumulates bits as the disk
spins until it has a "full" nibble
value (with the high bit set). Each bit
on disk takes 4 CPU cycles to come
around as the disk is spinning. That
means we need to read an 8-bit nibble
every 32 cycles. The data latch will
hold the "full" value for 4 more cycles
before it resets itself back to zero,
so we can spend an absolute maximum of
36 cycles on any one nibble before it's
gone.

Including the legitimate branches and
compares, combined with the "pointless"
PHA/PLA/NOP instructions, we spend 36
cycles before checking the data latch
again. That's too long; the data latch
will have reset itself to zero!

Except...

If there's an extra "0" bit after the
nibble, the data latch holds its value
for an extra 4 cycles.

So this code burns exactly the right
amount of time to distinguish whether a
nibble has a timing bit after it. Then
it does it 20 times to make sure it
wasn't a fluke.

Advanced nibble copiers like EDD4 and
Copy II Plus will preserve one timing
bit per nibble -- even inside the data
field, as they are on this disk. But of
course Passport didn't preserve any
nibbles -- it read sector data with the
original disk's own RWTS, then wrote
out new sector data. That sector data
doesn't have any timing bits at all, so
the check fails.

There's no consequence to skipping the
check. I can change T00,S01 to jump
directly to $9D84 and bypass the check
altogether.

T00,S01,$48: 0EBF -> 849D

]PR#6
...works, and it is glorious...

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 1196
------------------EOF------------------
